if(!require("install.load")){
install.packages("install.load")
}
library(install.load)
install_load("readr", "dplyr", "ggplot2", "knitr","tidyverse","plotly","moments")
Wie ist der Logistikverzug verteilt?
Als erstes werden die nötigen Daten importiert.
Komponente_K7 <- read.csv2("Data/Logistikverzug/Komponente_K7.csv")
Logistikverzug_K7 <- read.csv("Data/Logistikverzug/Logistikverzug_K7.csv",sep=";")
In dieser Aufgabe untersuchen wir die Verteilung des Logistikverzugs der Komponente K7. Zunächst einmal werden die beiden eingelesenden Datasets zu Logistikverzug zusammengefügt. Dies passiert unter der Angabe der IDNummer. Im nächsten Schritt werden die Produktionsdaten gefiltert, sodass nur die Daten von 2014 bis 2016 betrachtet werden.
Logistikverzug <- merge(x = Komponente_K7[,c("IDNummer", "Produktionsdatum")], y = Logistikverzug_K7[ , c("IDNummer","Wareneingang")], by = "IDNummer")
Logistikverzug <- Logistikverzug %>%
filter(Produktionsdatum >= as.Date("2014-01-01") & Produktionsdatum <= as.Date("2016-12-31"))
Zur besseren Übersicht und zum Vergleich wird nun das Datumsformat für den Wareneingang angepasst. Es wird das Format Y-m-d genutzt.
Logistikverzug$Wareneingang <- as.Date(strptime(Logistikverzug$Wareneingang, format = "%d.%m.%Y"), format = "%Y-%m-%a")
Nun wird der Verzug berechnet. Hierbei ist zu beachten, dass zwischen dem Produktionsdatum und dem Warenausgangsdatum 2 Werktage liegen, da der Hersteller 1 Tag für die Übergabe zur Logistikabteilung und ebenfalls 1 Tag für die Versandvorbereitung benötigt. Da der Sonntag kein Werktag ist, ist dieser von der Rechnung ausgeschlossen.
Da der Sonntag kein Werktag ist, muss dieser aus dem Verzug rausgerechnet werden. Dafür wird die Funktion anzahl_sonntage bestimmt. Sie berechnet die Anzahl der Sonntage zwischen dem Produktionsdatum und dem Enddatum. Danach wird eine neue Spalte namens Verzug_ohne_Sonntage erstellt, indem für jeden Eintrag in “Logistikverzug” die Differenz in Tagen zwischen dem Wareneingangsdatum und dem Produktionsdatum berechnet wird. Diese Differenz wird dann um die Anzahl der Sonntage zwischen diesen beiden Daten reduziert. Das Ergebnis wird in der Spalte Verzug_ohne_Sonntage gespeichert.
anzahl_sonntage <- function(produktionsdatum, enddatum) {
produktionsdatum <- as.Date(produktionsdatum)
tage <- seq.Date(from = produktionsdatum, to = enddatum, by = "day")
sum(weekdays(tage) == "Sunday")
}
Die Spalte “gesamt” wird erstellt, um den Gesamtverzug für jeden Eintrag zu berechnen. Dabei wird einfach die Differenz in Tagen zwischen dem Wareneingangsdatum und dem Produktionsdatum berechnet und gespeichert. Durch die Verwendung von sapply werden die Berechnungen für jeden Eintrag in “Logistikverzug” durchgeführt. Im Endeffekt wird vom Gesamtverzug die Anzahl der Sonntage und die 2 Tage für das Übergeben an die Logistikabteilung/Versandvorbereitung abgezogen.
Logistikverzug$Verzug_ohne_Sonntage <- sapply(1:nrow(Logistikverzug), function(i) {
gesamtverzug <- as.integer(difftime(Logistikverzug$Wareneingang[i], Logistikverzug$Produktionsdatum[i], units = "days"))
sonntage <- anzahl_sonntage(Logistikverzug$Produktionsdatum[i], Logistikverzug$Wareneingang[i])
gesamtverzug - sonntage - 2
})
Logistikverzug$gesamt <- sapply(1:nrow(Logistikverzug), function(i) {
gesamtverzug <- as.integer(difftime(Logistikverzug$Wareneingang[i], Logistikverzug$Produktionsdatum[i], units = "days"))
})
Nun wird ein Histogramm mit den Verzugsdaten dargestellt:
hist(Logistikverzug$Verzug_ohne_Sonntage, breaks = 10, main = "Histogramm des Logistikverzugs", xlab = "Verzug in Tagen", ylab = "Anzahl")
Das Histogramm deutet darauf hin, dass die logistische Verzögerung einer Normalverteilung folgen könnte. Dies ist durch eine glockenähnliche Struktur der Balken zu erkennen.
Ein Q-Q-Plot kann verwendet werden, um die Annahme zu überprüfen, dass die Daten des logistischen Verzugs einer bestimmten theoretischen Verteilung folgen. Der Q-Q-Plot vergleicht die empirischen Quantile der Daten mit den theoretischen Quantilen der angenommenen Verteilung. Wenn die empirischen und theoretischen Quantile eng beieinander liegen, deutet dies darauf hin, dass die empirischen Daten ähnlich wie die angenommene Verteilung verteilt sind.
Verzug_arbeitstage <- Logistikverzug$Verzug_ohne_Sonntage
qqnorm(Verzug_arbeitstage)
qqline(Verzug_arbeitstage, col="red")
Der Q-Q-Plot zeigt, dass die Daten nahe an der linearen roten Linie
liegen, jedoch sind einige zum Teil erhebliche Abweichungen zu
erkennen.
Um diese nahe Übereinstimmung weiter zu überprüfen, wird nun der Shapiro-Wilk-Test hinzugezogen. Dabei wird aus den Daten eine zufällige Stichprobe von 5000 Datenpunkten verwendet:
shapiro.test(sample(Verzug_arbeitstage, 5000))
##
## Shapiro-Wilk normality test
##
## data: sample(Verzug_arbeitstage, 5000)
## W = 0.89737, p-value < 2.2e-16
Der Shapiro Test ist auf eine maximale Stichprobe von 5000 begrenzt. Das ist in diesem Fall ausreichend, da mit diesem Stichprobenumfang bereits eine gute Präzision erreicht wird. Der Wert W beträgt etwa 0,9. Das ist nah am Maximalwert 1 und das zeigt zunächst einmal eine gute Übereinstimmung mit der Normalverteilung. Der p-Wert = 2.2e-16 ist hingegen viel kleiner als das Signifikanzniveau 0.05. Die Abweichung ist so gravierend, dass die Normalverteilung eindeutig verworfen wird. Die Ergebnisse der bisherigen Tests sind nicht weiter verwunderlich, da eindeutig zu erkennen ist, dass viele Datenpaare auf der rechten Seite des Maximas liegen, sozusagen einen Schwanz ausbilden, während nach links die Werte durch die 0 begrenzt sind. Das deutet auf eine schiefe Verteilung hin, welche der Normalverteilung ähnelt, was auch erklärt, weswegen viele Testergebnisse auf eine Normalverteilung hingedeutet haben.
Um auf eine schiefe Verteilung zu testen, eignet sich der Skewness-Test:
skewness_test <- skewness(Verzug_arbeitstage)
skewness_test
## [1] 0.5610644
Der Wert ist deutlich größer Null und indiziert eine rechtsschiefe Verteilung.
Ergebnis Die Daten liegen unter einer rechtsschiefen Verteilung vor.
Wie viel Zeit vergeht mindestens zwischen Warenausgang und Wareneingang?
Hier können die Extremwerte mittels min() und max() von Verzug_arbeitstage einfach erstellt werden.
minimaler_Verzug <- min(Verzug_arbeitstage)
print(minimaler_Verzug)
## [1] 0
Der minimale Verzug ist 0 Werktage.
maximaler_verzug <- max(Verzug_arbeitstage)
print(maximaler_verzug)
## [1] 10
Der maximale Verzug ist 10 Werktage.
Bestimmen Sie den Mittelwert des Logistikverzugs
Hier wird nun der Mittelwert des Logistiksverzugs bestimmt:
mittelwert_verzug <- mean(Verzug_arbeitstage)
print(mittelwert_verzug)
## [1] 3.080826
Der Mittelwert des Verzugs liegt bei etwa 3.08 Werktage, also knapp über 3 Werktage.
Stellen Sie die Vertilung in geeigneter Weise mit Plotly dar
fig <- plot_ly(x = Verzug_arbeitstage, type = "histogram", nbinsx = 50)
fig <- fig %>%
layout(
title = "Histogramm des Verzugs",
xaxis = list(title = "Verzug in Tagen"),
yaxis = list(title = "Häufigkeit")
)
fig
Warum ist es sinnvoll, die Ihnen bereitgestellten Daten in separaten Dateien abzulegen und nicht alles in einer riesigen Tabelle abzuspeichern? Wie nennt sich das zugrunde liegende Datenbankkonzept?
Das zugrundeliegende Datenbankkonzept wird als relationales Datenbankmodell bezeichnet. Dieses bietet folgende Vorteile:
1. Effiziente Datenverwaltung: Durch die Organisation der Daten in mehrere Tabellen können die Daten effizienter verwaltet werden. Jede Tabelle kann sich auf ein bestimmtes Thema konzentrieren, was die Datenbankstruktur übersichtlicher und leichter zu warten macht.
2. Unterstützung für komplexe Abfragen: Komplexe Abfragen über mehrere Tabellen hinweg sind möglich. Dadurch können umfassende Analysen durchführt und Informationen aus verschiedenen Teilen der Datenbank effizient extrahiert werden.
3. Bessere Skalierbarkeit: Das relationale Datenbankmodell bietet eine bessere Skalierbarkeit, da neue Tabellen hinzufügt oder vorhandene Tabellen angepasst werden können, ohne die gesamte Datenbankstruktur zu beeinträchtigen. Dies erleichtert es, mit zunehmendem Datenwachstum umzugehen und die Leistung der Datenbank zu optimieren.
4. Verbesserte Datenkonsistenz: Es können Beziehungen zwischen den verschiedenen Tabellen hergestellt werden. Dies ermöglicht es, Daten zu verknüpfen und Abhängigkeiten zwischen den verschiedenen Datenobjekten zu modellieren. Das verbessert die Datenkonsistenz.
Wie viele der Komponenten K7 landeten in Fahrzeugen, die in Wehr, Landkreis Waldshut zugelassen wurden?
Als erstes wird die Funktion lese_bestandteile_datei definiert, die den Pfad zu den Komponentendateien basierend auf dem Hersteller (OEM) und dem Typ der Komponente erhält und dann die entsprechende CSV-Datei einliest.
lese_bestandteile_datei <- function(oem, typ) {
file_path <- paste0("Data/Fahrzeug/Bestandteile_Fahrzeuge_", oem, "_Typ", typ, ".csv")
read_csv2(file_path)
}
Anschließend werden verschiedene Dateien eingelesen. Bei Bauteil K7 handelt es sich um die Karosserie. Aus der Komponente_K7.txt-Datei wird erstmal nur die Spalte ID_Karosserie benötigt. Die benötigte Spalte ID_Karosserie wird mittels select() gewählt und aus der TXT-Datei eingelesen und in ein tibble konvertiert. Die relevanten Spalten der Zulassungen-Datei werden ebenfalls mit select() ausgewählt und aus der Zulassungen_alle_Fahrzeuge.csv Datei eingelesen. Die Spalte IDNummer wird zu ID_Fahrzeug umbenannt, um später Daten mithilfe dieser Spalte verbinden zu können.
k7_neu <- read.table("Data/Komponente/Komponente_K7.txt", sep = "\t", stringsAsFactors = FALSE) %>%
as_tibble() %>%
dplyr::select(ID_Karosserie)
zulassungen_neu <- read_csv2("Data/Zulassungen/Zulassungen_alle_Fahrzeuge.csv")%>% dplyr::select(ID_Fahrzeug = IDNummer, Gemeinde = Gemeinden, Zulassung)
Nun wird die lese_bestandteile_datei Funktion verwendet, um die komponenten_OEM-Dateien einzulesen. Es werden nur die Spalten ID_Fahrzeug und ID_Karosserie ausgewählt. Die Dataframes der verschiedenen OEMs und Typen werden mittels bind_rows zu einem einzigen Dataframe namens komponenten_gesamt zusammengeführt.
komponenten_gesamt <- bind_rows(
komponenten_OEM1_typ11_neu <- lese_bestandteile_datei("OEM1", "11")%>% dplyr::select(ID_Fahrzeug, ID_Karosserie),
komponenten_OEM1_typ12_neu <- lese_bestandteile_datei("OEM1", "12")%>% dplyr::select(ID_Fahrzeug, ID_Karosserie),
komponenten_OEM2_typ21_neu <- lese_bestandteile_datei("OEM2", "21")%>% dplyr::select(ID_Fahrzeug, ID_Karosserie),
komponenten_OEM2_typ22_neu <- lese_bestandteile_datei("OEM2", "22")%>% dplyr::select(ID_Fahrzeug, ID_Karosserie)
)
Im Folgenden werden k7_neu, komponenten_gesamt und zulassungen_neu mittels left_join zusammengeführt.
enddaten <- left_join(k7_neu, komponenten_gesamt, by = "ID_Karosserie") %>%
left_join(zulassungen_neu, by = "ID_Fahrzeug")
Schließlich wird der gesamte Datenframe gefiltert, um nur die Fahrzeuge zu behalten, die in der Gemeinde “WEHR” zugelassen sind. Die Anzahl dieser Fahrzeuge wird ermittelt und als Ergebnis ausgegeben.
fahrzeuge_wehr_k7 <- enddaten %>%
filter(Gemeinde == "WEHR") %>%
summarise(Anzahl_Fahrzeuge_in_Wehr = n())
fahrzeuge_wehr_k7
## # A tibble: 1 × 1
## Anzahl_Fahrzeuge_in_Wehr
## <int>
## 1 6
Es gibt 6 Fahrzeuge, die die Komponente K7 enthalten und in Wehr zugelassen wurden.
Welche Datentypen haben die Attribute der Zulassungstabelle „Zulassungen_aller_Fahrzeuge“? Erstellen Sie dazu eine Tabelle in Markdown.
Um den Datentyp zu identifizieren, kann die Funktion str() genutzt werden. Colnames() soll die relevanten Spaltennamen ausgeben.
zulassungen <- read_csv2("Data/Zulassungen/Zulassungen_alle_Fahrzeuge.csv")
str(zulassungen)
## spc_tbl_ [3,204,104 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ ...1 : num [1:3204104] 408097 408098 1 2 3 ...
## $ IDNummer : chr [1:3204104] "11-1-11-1" "11-1-11-2" "12-1-12-1" "12-1-12-2" ...
## $ Gemeinden: chr [1:3204104] "DRESDEN" "DRESDEN" "LEIPZIG" "LEIPZIG" ...
## $ Zulassung: Date[1:3204104], format: "2009-01-01" "2009-01-01" ...
## - attr(*, "spec")=
## .. cols(
## .. ...1 = col_double(),
## .. IDNummer = col_character(),
## .. Gemeinden = col_character(),
## .. Zulassung = col_date(format = "")
## .. )
## - attr(*, "problems")=<externalptr>
colnames(zulassungen)
## [1] "...1" "IDNummer" "Gemeinden" "Zulassung"
Es ist zu erkennen, dass die Zulassungstabelle 4 Spalten besitzt: “…1” als numeric double, “IDNummer” als character, “Gemeinde” als character und “Zulassung” als date.
Hier wird die dazugehörige Tabelle erstellt, die die Spaltennamen und die entsprechenden Datentypen enthält. Dies geschieht durch Extrahieren der Klassennamen der einzelnen Spalten mit der Funktion class() und anschließendes Zusammenführen in einem Dataframe. Die Tabelle wird schließlich mit kable() formatiert und ausgegeben.
spaltennamen_zulassung <- names(zulassungen)
datentypen_zulassungen <- c(class(zulassungen$...1), class(zulassungen$IDNummer), class(zulassungen$Gemeinden), class(zulassungen$Zulassung))
tabelle <- data.frame(Spaltenname = spaltennamen_zulassung, Datentyp = datentypen_zulassungen)
kable(
tabelle,
caption = "Zulassung aller Fahrzeuge",
format = "markdown",
booktabs = TRUE
)
| Spaltenname | Datentyp |
|---|---|
| …1 | numeric |
| IDNummer | character |
| Gemeinden | character |
| Zulassung | Date |
Sie wollen Ihre Applikation veröffentlichen. Warum ist es gerade dann sinnvoll die Datensätze auf der Datenbank eines Servers abzulegen?
Verfügbarkeit: Ein Server kann durchgehend online sein. Die Benutzer greifen von verschiedenen Orten auf dieselben Daten zu, was Dateninkonsistenzen vermeidet.
Datenorganisation: Die Verwendung einer Server-Datenbank ermöglicht eine strukturierte Organisation und Speicherung von Daten. Dies erleichtert die Verwaltung, Aktualisierung und Pflege der Daten, selbst wenn die Benutzerzahl begrenzt ist.
Warum können Sie die Datensätze nicht auf Ihrem persönlichen Computer ablegen?
Begrenzte Ressourcen: Persönliche Computer haben begrenzte Ressourcen wie CPU, RAM und Festplattenspeicher. Wenn die Datenmengen groß sind oder viele Benutzer auf die Anwendung zugreifen, kann dies zu Leistungsproblemen führen.
Datenverlust: Persönliche Computer sind anfälliger für Datenverlust durch Hardwarefehler, Softwareprobleme oder Diebstahl. Daten, die ausschließlich auf einem persönlichen Computer gespeichert sind, können leichter verloren gehen.
Nennen Sie eine einfache Möglichkeit Ihre Applikation ihrem Kundenkreis zugänglich zu machen?
Am 11.08.2010 hat es einen Unfall mit Fahrerflucht gegeben. Von dem Kennzeichen des Unfallwagens fehlt jede Spur. Die Polizei bittet Sie um Hilfe, da Sie für das Kraftfahrtbundesamt arbeiten und fragt, wo das Fahrzeug mit der Karosseriebauteilnummer „K7-114-1142-31“ zugelassen wurde.
Der folgende Code filtert Fahrzeugzulassungen basierend auf der Karosseriebauteilnummer “K7-114-1142-31”. Zuerst wird die entsprechende Fahrzeug-ID aus den Komponentendaten extrahiert. Dann werden die Zulassungseinträge gefiltert, die dieser ID entsprechen.
ort <- filter(zulassungen_neu, ID_Fahrzeug %in% filter(komponenten_gesamt, ID_Karosserie == "K7-114-1142-31")$ID_Fahrzeug)
print(ort)
## # A tibble: 1 × 3
## ID_Fahrzeug Gemeinde Zulassung
## <chr> <chr> <date>
## 1 22-2-22-2 ALLENDORF (EDER) 2009-01-02
Das Fahrzeug mit der Kassoriebauteilnummer „K7-114-1142-31“ wurde in Allendorf (Eder) zugelassen.